﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Epsilon.Expr;

namespace Epsilon.Typing
{
    public class RxStore
    {
        private Dictionary<RxClauseExpr, PidRLabel> rxPidMax;
        private Dictionary<RxClauseExpr, PidRLabel> rxPidMin;
        private Dictionary<RxClauseExpr, SecRLabel> rxSec;

        public RxStore()
        {
            rxPidMax = new Dictionary<RxClauseExpr, PidRLabel>();
            rxPidMin = new Dictionary<RxClauseExpr, PidRLabel>();
            rxSec = new Dictionary<RxClauseExpr, SecRLabel>();
        }

        public List<RxClauseExpr> GetAllClauses()
        {
            return rxPidMax.Keys.ToList();
        }

        public PidRLabel GetRxPidMaxArg(RxClauseExpr e)
        {
            if (!rxPidMax.ContainsKey(e))
                AddRxPidMaxArg(e);

            return rxPidMax[e];
        }

        public PidRLabel GetRxPidMinArg(RxClauseExpr e)
        {
            if (!rxPidMin.ContainsKey(e))
                AddRxPidMinArg(e);

            return rxPidMin[e];
        }

        public SecRLabel GetRxSecArg(RxClauseExpr e)
        {
            if (!rxSec.ContainsKey(e))
                AddRxSecArg(e);

            return rxSec[e];
        }

        private void AddRxPidMaxArg(RxClauseExpr e)
        {
            rxPidMax.Add(e, PidUtil.CreateFromType(e.Type));
        }

        private void AddRxPidMinArg(RxClauseExpr e)
        {
            rxPidMin.Add(e, PidUtil.CreateFromType(e.Type));
        }

        private void AddRxSecArg(RxClauseExpr e)
        {
            rxSec.Add(e, SecUtil.CreateFromType(e.Type));
        }
    }

    public class FunStore
    {
        private Dictionary<FunExpr, List<PidRLabel>> fPidMax;
        private Dictionary<FunExpr, List<PidRLabel>> fPidMin;
        private Dictionary<FunExpr, List<SecRLabel>> fSec;

        public FunStore()
        {
            fPidMax = new Dictionary<FunExpr, List<PidRLabel>>();
            fPidMin = new Dictionary<FunExpr, List<PidRLabel>>();
            fSec = new Dictionary<FunExpr, List<SecRLabel>>();
        }

        public List<PidRLabel> GetFunPidMaxArg(FunExpr e)
        {
            if (!fPidMax.ContainsKey(e))
                AddFunPidMaxArg(e);

            return fPidMax[e];
        }

        public List<PidRLabel> GetFunPidMinArg(FunExpr e)
        {
            if (!fPidMax.ContainsKey(e))
                AddFunPidMinArg(e);

            return fPidMin[e];
        }

        public List<SecRLabel> GetFunSecArg(FunExpr e)
        {
            if (!fSec.ContainsKey(e))
                AddFunSecArg(e);

            return fSec[e];
        }

        private void AddFunPidMaxArg(FunExpr e)
        {
            List<PidRLabel> args = PidUtil.CreateFromType(new TupleType(e.ArgumentTypes)).Labels;
            fPidMax.Add(e, args);
        }

        private void AddFunPidMinArg(FunExpr e)
        {
            List<PidRLabel> args = PidUtil.CreateFromType(new TupleType(e.ArgumentTypes)).Labels;
            fPidMin.Add(e, args);
        }

        private void AddFunSecArg(FunExpr e)
        {
            List<SecRLabel> args = SecUtil.CreateFromType(new TupleType(e.ArgumentTypes)).Labels;
            fSec.Add(e, args);
        }
    }

    public class FunctionStore
    {
        private List<FunctionExpr> functions;
        private List<PidFun> pidMax = new List<PidFun>();
        private List<PidFun> pidMin = new List<PidFun>();
        private List<SecFun> sec = new List<SecFun>();

        public FunctionStore()
        {
            functions = new List<FunctionExpr>();
        }

        public int ContainsFunction(string name, List<Type> args)
        {
            for (int j = 0; j < functions.Count; j++) 
            {
                FunctionExpr f = functions[j];

                if (f.Name != name || f.Arguments.Count != args.Count)
                    continue;

                List<Type> types = f.ArgumentTypes;
                bool same = true;
                for (int i = 0; i < args.Count; i++)
                {
                    if (types[i] != args[i])
                    {
                        same = false;
                        break;
                    }
                }
                if (same)
                    return j;
            }
            return -1;
        }

        public void AddFunction(FunctionExpr f)
        {
            if (ContainsFunction(f.Name, f.ArgumentTypes) >= 0)
                throw new FunctionException("A function with the name " + f.Name + " has already been defined with the same argument types");

            functions.Add(f);

            // now calculate the labels for each function
            TupleType t = new TupleType(f.ArgumentTypes);
            List<PidRLabel> maxArgs = PidUtil.CreateFromType(t).Labels;
            pidMax.Add(new PidFun(maxArgs, new PidBase(null, null)));

            List<PidRLabel> minArgs = PidUtil.CreateFromType(t).Labels;
            pidMin.Add(new PidFun(minArgs, new PidBase(null, null)));

            List<SecRLabel> secArgs = SecUtil.CreateFromType(t).Labels;
            sec.Add(new SecFun(secArgs, new SecBase(null, null)));
        }

        public Tuple<FunType, PidFun, PidFun, SecFun> GetLabels(string name, List<Type> args)
        {
            int index = ContainsFunction(name, args);
            if (index < 0)
                throw new FunctionException("A function with the name " + name + " does not exist with the given argument types");

            return new Tuple<FunType, PidFun, PidFun, SecFun>(
                new FunType(functions[index].ArgumentTypes, functions[index].ReturnType),
                pidMax[index], pidMin[index], sec[index]);
        }

        public void UpdateLabels(string name, List<Type> args, PidLabel pMax, PidLabel pMin, SecLabel sec)
        {
            int index = ContainsFunction(name, args);
            if (index < 0)
                throw new FunctionException("A function with the name " + name + " does not exist with the given argument types");

            this.pidMax[index] = new PidFun(this.pidMax[index].Args, pMax);
            this.pidMin[index] = new PidFun(this.pidMin[index].Args, pMin);
            this.sec[index] = new SecFun(this.sec[index].Args, sec);
        }
    }

    public class SpecStore
    {
        private List<string> names = new List<string>();
        private List<FunType> functions = new List<FunType>();
        private List<PidFun> pidMax = new List<PidFun>();
        private List<PidFun> pidMin = new List<PidFun>();
        private List<SecFun> sec = new List<SecFun>();

        public SpecStore()
        {
        }

        public int ContainsFunction(string name, List<Type> args)
        {
            for (int j = 0; j < names.Count; j++)
            {
                FunType f = functions[j];

                if (names[j] != name || f.Args.Count != args.Count)
                    continue;

                List<Type> types = f.Args;
                bool same = true;
                for (int i = 0; i < args.Count; i++)
                {
                    if (types[i] != args[i])
                    {
                        same = false;
                        break;
                    }
                }
                if (same)
                    return j;
            }
            return -1;
        }

        public void AddSpec(string name, TypeLabel label)
        {
            if (!(label.Type is FunType) || !(label.MaxPidLabel is PidFun)
                || !(label.MinPidLabel is PidFun) || !(label.SecLabel is SecFun))
                throw new FunctionException("A function spec must have function labels (" + name + ")");

            FunType f = (FunType)label.Type;
            PidFun pmax = (PidFun)label.MaxPidLabel;
            PidFun pmin = (PidFun)label.MinPidLabel;
            SecFun secf = (SecFun)label.SecLabel;


            if (ContainsFunction(name, f.Args) >= 0)
                throw new FunctionException("A function spec with the name " + name + " has already been defined with the same argument types");

            names.Add(name);
            functions.Add(f);
            pidMax.Add(pmax);
            pidMin.Add(pmin);
            sec.Add(secf);
        }

        public Tuple<FunType, PidFun, PidFun, SecFun> GetLabels(string name, List<Type> args)
        {
            int index = ContainsFunction(name, args);
            if (index < 0)
                throw new FunctionException("A function with the name " + name + " does not exist with the given argument types");

            return new Tuple<FunType, PidFun, PidFun, SecFun>(
                new FunType(functions[index].Args, functions[index].Ret),
                pidMax[index], pidMin[index], sec[index]);
        }
    }
}
